home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / fsstat / fsstat.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-15  |  23.6 KB  |  709 lines

  1. /* 
  2.  * fsstat.c --
  3.  *
  4.  *    Print out the statistics kept for the filesystem.
  5.  *
  6.  * Copyright (C) 1986 Regents of the University of California
  7.  * All rights reserved.
  8.  */
  9.  
  10. #ifndef lint
  11. static char rcsid[] = "$Header: /sprite/src/cmds/fsstat/RCS/fsstat.c,v 1.8 92/05/15 11:10:08 kupfer Exp $ SPRITE (Berkeley)";
  12. #endif not lint
  13.  
  14. #include <sprite.h>
  15. #include <fs.h>
  16. #include <fsCmd.h>
  17. #include <stdio.h>
  18. #include <option.h>
  19. #include <vm.h>
  20. #include <sysStats.h>
  21. #include <kernel/vm.h>
  22. #include <kernel/fs.h>
  23. #include <kernel/fsStat.h>
  24.  
  25. Boolean deleteHist = FALSE;
  26. Boolean printHostInfo = FALSE;
  27. /*
  28.  * Parameters for printing histogram.  This will catch all times we'll
  29.  * encounter and will fit everything in an 80-column screen.  The program
  30.  * will still check for non-zero values outside this region and print
  31.  * a warning if one is encountered.  DEFAULT_WIDTH is 79 since we'd rather
  32.  * not print in the last column, and each entry takes 5 chars.
  33.  */
  34.  
  35. #define DEFAULT_WIDTH 79
  36.  
  37. int printExtraFileStats = FALSE;
  38. int numTimesToPrint = FS_HIST_TIME_BUCKETS; /* by default, print everything. */
  39. int numSizesToPrint = DEFAULT_WIDTH/5;
  40.  
  41. Option optionArray[] = {
  42.     {OPT_TRUE, "d", (Address)&deleteHist, "Print deletion histogram"},
  43.     {OPT_TRUE, "H", (Address)&printHostInfo, "Print kernel version and uptime info"},
  44.     {OPT_TRUE, "F", (Address)&printExtraFileStats, "Print file life-time info"},
  45.     {OPT_INT, "T", (Address)&numTimesToPrint,
  46.          "Number of times for which to print data"},
  47.     {OPT_INT, "S", (Address)&numSizesToPrint,
  48.          "Number of sizes for which to print data"},
  49. };
  50. int numOptions = sizeof(optionArray) / sizeof(Option);
  51.  
  52. ReturnStatus status;
  53.  
  54. Fs_Stats fsStats;
  55. Vm_Stat    vmStats;
  56. Fs_TypeStats fsTypeStats;
  57.  
  58. int GetRatio();
  59.  
  60. #define PrintRatio(x,y) printf("  %11d %3d%%", (x), GetRatio((x), (y)))
  61.  
  62. main(argc, argv)
  63.     int argc;
  64.     char *argv[];
  65. {
  66.     int status = SUCCESS;
  67.     register int t1;
  68.     int i;
  69.     unsigned int vmBlocksWritten;
  70.     unsigned int vmBlocksRead;
  71.     int pageSize;
  72.     char          version[128];
  73.     char          hostname[128];
  74.     double overflowFactor;
  75.  
  76.     argc = Opt_Parse(argc, argv, optionArray, numOptions);
  77.  
  78.     /*
  79.      * For "pseudo-double" variables -- two integers -- we need to store
  80.      * them in this program as real double variables.  The problem is,
  81.      * we can't just convert 0x80000000 from an unsigned int to float, because
  82.      * our C compiler goofs and makes the result negative.  Therefore, some
  83.      * tricks to take the FS_STAT_OVERFLOW constant and turn it into a number
  84.      * we can use to multiply by the overflow counter.  Shift it right, then
  85.      * multiply it by the equivalent number once it's a "double".  
  86.      *
  87.      * Note: the definition of FS_STAT_OVERFLOW confuses the gcc compiler
  88.      *    and results in a negative value of overflowFactor if used directly,
  89.      *    even after shifting and multiplying.  The definition is:
  90.      *    FS_STAT_OVERFLOW (1 << (sizeof(unsigned int) * 8 - 1))
  91.      */
  92.     overflowFactor = (0x80000000 >> 1) * 2.0;
  93.     
  94.     if (printHostInfo) {
  95.     if (Sys_Stats(SYS_GET_VERSION_STRING, sizeof(version), version) ==
  96.         SUCCESS){
  97.         printf("Kernel version: %s\n", version);
  98.     }
  99.     fflush(stdout);
  100.     (void)system("date;loadavg");
  101.     }
  102.  
  103.     status = Fs_Command(FS_RETURN_STATS, sizeof(Fs_Stats), &fsStats);
  104.     if (status != SUCCESS) {
  105.     Stat_PrintMsg(status, "Fs_Command failed");
  106.     exit(status);
  107.     }
  108.     status = Vm_Cmd(VM_GET_STATS, &vmStats);
  109.     if (status != SUCCESS) {
  110.     Stat_PrintMsg(status, "Vm_Cmd failed");
  111.     exit(status);
  112.     }
  113.     Vm_PageSize(&pageSize);
  114.     vmBlocksWritten = vmStats.pagesWritten * (pageSize / FS_BLOCK_SIZE);
  115.     if (printExtraFileStats) {
  116.     status = Fs_Command(FS_RETURN_LIFE_TIMES, sizeof(Fs_TypeStats), &fsTypeStats);
  117.     if (status != SUCCESS) {
  118.         fprintf(stderr, "This kernel doesn't do FS_RETURN_LIFE_TIMES\n");
  119.         printExtraFileStats = 0;
  120.     }
  121.     }
  122.  
  123.  
  124.   {
  125.     register Fs_BlockCacheStats *block;
  126.     
  127.     block = &fsStats.blockCache;
  128.     printf("Block Cache, %.2f Mbytes\n", (float)block->numCacheBlocks *
  129.                 FS_BLOCK_SIZE / (1024 * 1024));
  130.     printf(" BLOCKS %u min %u max %u(%u) free %u\n",
  131.         block->numCacheBlocks, block->minCacheBlocks,
  132.         block->maxCacheBlocks, block->maxNumBlocks,
  133.         block->numFreeBlocks);
  134.     t1 = block->readHitsOnDirtyBlock + block->readHitsOnCleanBlock;
  135.     printf(" READS   %8u dr_hits %8u cl_hits  %8u\t\thit ratio %u\n",
  136.         block->readAccesses,
  137.         block->readHitsOnDirtyBlock,
  138.         block->readHitsOnCleanBlock,
  139.         100 * t1 / block->readAccesses);
  140.     printf(" WRITES  %8u  p-hits %8u p-misses %8u\t\ttfc ratio %u\n",
  141.         block->writeAccesses,
  142.         block->partialWriteHits,
  143.         block->partialWriteMisses,
  144.         block->writeAccesses ?
  145.         100 * block->blocksWrittenThru / block->writeAccesses : 0);
  146.     printf(" WRITETHRU %u ", block->blocksWrittenThru);
  147.     if (block->blocksWrittenThru) {
  148.     printf("data %u %u%% index %u %u%% desc %u %u%% dir %u %u%% vm %u %u%%\n",
  149.         block->dataBlocksWrittenThru,
  150.         100 * block->dataBlocksWrittenThru / block->writeAccesses,
  151.         block->indBlocksWrittenThru,
  152.         100 * block->indBlocksWrittenThru / block->writeAccesses,
  153.         block->descBlocksWrittenThru,
  154.         100 * block->descBlocksWrittenThru / block->writeAccesses,
  155.         block->dirBlocksWrittenThru,
  156.         100 * block->dirBlocksWrittenThru / block->writeAccesses,
  157.         vmBlocksWritten,
  158.         100 * vmBlocksWritten / block->writeAccesses);
  159.     }
  160.     printf(" ZERO FILL read %u write %u/%u append %u over %u\n",
  161.         block->readZeroFills,
  162.         block->writeZeroFills1, block->writeZeroFills2,
  163.         block->appendWrites,
  164.         block->overWrites);
  165.     printf(" READ AHEAD %u hits %u all in cache %u/%u\t\t\thit ratio %u\n",
  166.         block->readAheads,
  167.         block->readAheadHits,
  168.         block->allInCacheCalls,
  169.         block->allInCacheTrue,
  170.         block->readAheads ?
  171.         100 * block->readAheadHits / block->readAheads : 0);
  172.     printf(" FRAGMENT upgrades %u hits %u zero fills %u\n",
  173.         block->fragAccesses,
  174.         block->fragHits,
  175.         block->fragZeroFills);
  176.     if (block->fileDescReads > 0) {
  177.     printf(" FILE DESC reads %u hits %u (%2u%%) writes %u hits %u (%2u%%)\n", 
  178.             block->fileDescReads, block->fileDescReadHits,
  179.             100 * block->fileDescReadHits / block->fileDescReads,
  180.             block->fileDescWrites, block->fileDescWriteHits,
  181.             block->fileDescWrites ?
  182.             100 * block->fileDescWriteHits / block->fileDescWrites
  183.             : 0);
  184.     }
  185.     if (block->indBlockAccesses > 0) {
  186.     printf(" INDIRECT BLOCKS  Accesses %u hits %u\t\t\thit ratio %u\n", 
  187.             block->indBlockAccesses, block->indBlockHits,
  188.             100 * block->indBlockHits / block->indBlockAccesses);
  189.     }
  190.     if (block->dirBlockAccesses > 0) {
  191.     printf(" DIRECTORY BLOCKS Accesses %u hits %u\t\t\thit ratio %u\n", 
  192.             block->dirBlockAccesses, block->dirBlockHits,
  193.             100 * block->dirBlockHits / block->dirBlockAccesses);
  194.     }
  195.     printf(" VM asked %u we tried %u gave up %u Pitched %u\n",
  196.         block->vmRequests, block->triedToGiveToVM, block->vmGotPage,
  197.         block->blocksPitched);
  198.     printf(" ALLOC from free %u new %u lru %u part free %u\n",
  199.         block->totFree, block->unmapped,
  200.         block->lru, block->partFree);
  201.   }
  202.   {
  203.     register Fs_AllocStats *alloc = &fsStats.alloc;
  204.     if (alloc->blocksAllocated > 0) {
  205.     printf("Block Allocation Statistics:\n");
  206.     printf(" BLOCKS alloc %u free %u\n", alloc->blocksAllocated,
  207.            alloc->blocksFreed);
  208.     printf(" CYLINDERS searched %u hashes %u bit-searches %u\n",
  209.            alloc->cylsSearched, alloc->cylHashes,
  210.            alloc->cylBitmapSearches);
  211.     printf(
  212. " FRAGS alloc %u free %u fr->block %u block->fr %u upgrades %u bad hints %u\n",
  213.            alloc->fragsAllocated, alloc->fragsFreed,
  214.            alloc->fragToBlock, alloc->fullBlockFrags,
  215.            alloc->fragUpgrades, alloc->badFragList);
  216.     }
  217.   }
  218.   {
  219.     register Fs_NameOpStats *cltName = &fsStats.cltName;
  220.     printf("Client Naming Operations:\n");
  221.     printf(" Open R %u W %u RW %u chdir %d\n",
  222.         cltName->numReadOpens, cltName->numWriteOpens,
  223.         cltName->numReadWriteOpens,
  224.         cltName->chdirs);
  225.     printf(" Make dir %u dev %u hardLink %u symLink %u\n",
  226.         cltName->makeDirs, cltName->makeDevices,
  227.         cltName->hardLinks, cltName->symLinks);
  228.     printf(" Remove %u rename %u rmdir %u\n",
  229.         cltName->removes, cltName->renames,
  230.         cltName->removeDirs);
  231.     printf(" Get attr name %u stream %u Set attr name %u stream %u\n",
  232.         cltName->getAttrs, cltName->getAttrIDs,
  233.         cltName->setAttrs, cltName->setAttrIDs);
  234.   }
  235.   {
  236.     register Fs_NameOpStats *srvName = &fsStats.srvName;
  237.     if (srvName->numReadOpens > 0) {
  238.     printf("Server Naming Operations:\n");
  239.     printf(" Opens %u\n",
  240.             srvName->numReadOpens);
  241.     printf(" Make dir %u dev %u hardLink %u symLink %u\n",
  242.             srvName->makeDirs, srvName->makeDevices,
  243.             srvName->hardLinks, srvName->symLinks);
  244.     printf(" Remove %u rename %u rmdir %u\n",
  245.             srvName->removes, srvName->renames,
  246.             srvName->removeDirs);
  247.     printf(" Get attr name %u stream %u Set attr name %u stream %u\n",
  248.             srvName->getAttrs, srvName->getAttrIDs,
  249.             srvName->setAttrs, srvName->setAttrIDs);
  250.     printf(" Get I/O attr %u Set I/O attr %u\n",
  251.             srvName->getIOAttrs, srvName->setIOAttrs);
  252.     }
  253.   }
  254.   {
  255.     register Fs_LookupStats *lookup = &fsStats.lookup;
  256.     register Fs_NameCacheStats *name = &fsStats.nameCache;
  257.     register int lookupMisses;
  258.     if (lookup->number > 0) {
  259.     printf("Name Lookup Statistics:\n");
  260.     lookupMisses = lookup->notFound + lookup->redirect +
  261.              lookup->remote + lookup->parent;
  262.     printf(" Paths %u hits %d (%u%%) components %u avg %.2f $MACHINE %u\n",
  263.         lookup->number, lookup->number - lookupMisses,
  264.         100 * (lookup->number - lookupMisses) / lookup->number,
  265.         lookup->numComponents,
  266.         (float)lookup->numComponents / (float)lookup->number,
  267.         lookup->numSpecial);
  268.     printf(" For create %u delete %u link %u rename %u\n",
  269.         lookup->forCreate, lookup->forDelete,
  270.         lookup->forLink, lookup->forRename);
  271.     printf(" Not found %u link expand %u redirect %u remote %u parent %u\n",
  272.         lookup->notFound, lookup->symlinks,
  273.         lookup->redirect, lookup->remote, lookup->parent);
  274.     }
  275.     if (name->accesses > 0) {
  276.     printf(" Name Cache tries %u hits %u (%u%%) replaced %u (%u%%) size %u\n",
  277.            name->accesses, name->hits,
  278.            100 * name->hits / name->accesses,
  279.            name->replacements,
  280.            100 * name->replacements / name->accesses,
  281.            name->size);
  282.     }
  283.   }
  284.   {
  285.     register Fs_HandleStats *handle = &fsStats.handle;
  286.     printf("File Handle Statistics:\n");
  287.     printf(" Number %u create %u install %u hits %u version %u flush %u\n",
  288.            handle->exists, handle->created, handle->installCalls,
  289.            handle->installHits, handle->versionMismatch,
  290.            handle->cacheFlushes);
  291.     printf(" Fetch %u hits %u lock %u waits %u unlock %u release %u\n",
  292.            handle->fetchCalls, handle->fetchHits, handle->locks,
  293.            handle->lockWaits, handle->unlocks, handle->release);
  294.     printf(" Segment fetches %u hits %u\t\t\t\thit ratio %u\n",
  295.            handle->segmentFetches, handle->segmentHits,
  296.            GetRatio(handle->segmentHits, handle->segmentFetches));
  297.   }
  298.   {
  299.     register Fs_PrefixStats *prefix = &fsStats.prefix;
  300.     printf("Prefix Statistics:\n");
  301.     printf(" Absolute %u relative %u redirect %u loop %u timeout %u stale %u found %u\n",
  302.            prefix->absolute, prefix->relative, prefix->redirects,
  303.            prefix->loops, prefix->timeouts, prefix->stale, prefix->found);
  304.   }
  305.  
  306.   {
  307.     unsigned int    numBytes;
  308.     unsigned int    arr[3];
  309.  
  310.     status = Fs_Command(FS_GET_FRAG_INFO, sizeof(arr), arr);
  311.     if (status != SUCCESS) {
  312.     Stat_PrintMsg(status, "Call to Fs_Command for frag info failed");
  313.     exit(status);
  314.     }
  315.     numBytes = arr[0] * FS_BLOCK_SIZE;
  316.     printf("Internal fragmentation statistics: Total bytes %u\n", numBytes);
  317.     printf(" Total bytes wasted   %7u percent %u\n",
  318.         arr[1], arr[1] * 100 / numBytes);
  319.     printf(" Waste relative to 1K %7u percent %u\n",
  320.         arr[2], arr[2] * 100 / numBytes);
  321.   }
  322. #define NUM_BYTE_COUNT_COLUMNS 4
  323.  
  324.   {
  325.       register Fs_GeneralStats *gen = &fsStats.gen;
  326.       double cacheBytes, thruBytes;
  327.       double rmtRatio, diskRatio;
  328.     
  329.       printf("Bytes:      cache     remote           disk       raw disk        devices\n");
  330.       cacheBytes = fsStats.blockCache.bytesRead +
  331.           overflowFactor * fsStats.blockCache.bytesReadOverflow;
  332.       thruBytes = gen->remoteBytesRead +
  333.           overflowFactor * gen->remoteReadOverflow;
  334.       rmtRatio = (cacheBytes > 0) ? (100. * thruBytes / cacheBytes) : 0;
  335.       printf(" Mb Read  %8.2f %8.2f %3d%%", cacheBytes / (1024 * 1024),
  336.            thruBytes / (1024 * 1024), (unsigned int)rmtRatio);
  337.  
  338.       thruBytes = gen->fileBytesRead + overflowFactor * gen->fileReadOverflow;
  339.       diskRatio = (cacheBytes > 0) ? (100. * thruBytes / cacheBytes) : 0;
  340.       printf(" %8.2f %3d%%", thruBytes / (1024 * 1024),
  341.                   (unsigned int)diskRatio);
  342.  
  343.       thruBytes = gen->physBytesRead;
  344.       diskRatio = (cacheBytes > 0) ? (100. * thruBytes / cacheBytes) : 0;
  345.  
  346.       printf(" %8.2f %3d%%", thruBytes / (1024 * 1024),
  347.                   (unsigned int)diskRatio);
  348.  
  349.       printf(" %8.2f\n", (float)gen->deviceBytesRead / (1024 * 1024));
  350.  
  351.       cacheBytes = fsStats.blockCache.bytesWritten +
  352.           overflowFactor * fsStats.blockCache.bytesWrittenOverflow;
  353.       thruBytes = gen->remoteBytesWritten +
  354.           overflowFactor * gen->remoteWriteOverflow;
  355.       if (cacheBytes > 0) {
  356.       rmtRatio = 100. * thruBytes / cacheBytes;
  357.       } else {
  358.       rmtRatio = 0.;
  359.       }
  360.       printf(" Mb Write %8.2f %8.2f %3d%%", cacheBytes / (1024 * 1024),
  361.            thruBytes / (1024 * 1024), (unsigned int)rmtRatio);
  362.       thruBytes = gen->fileBytesWritten +
  363.           overflowFactor * gen->fileWriteOverflow;
  364.       if (fsStats.blockCache.bytesWritten > 0) {
  365.       diskRatio = 100. * thruBytes / cacheBytes;
  366.       } else {
  367.       diskRatio = 0.;
  368.       }
  369.       printf(" %8.2f %3d%%", thruBytes / (1024 * 1024),
  370.               (unsigned int)diskRatio);
  371.  
  372.       thruBytes = gen->physBytesWritten;
  373.       if (cacheBytes > 0) {
  374.       diskRatio = 100. * thruBytes / cacheBytes;
  375.       } else {
  376.       diskRatio = 0.;
  377.       }
  378.       printf(" %8.2f %3d%%", thruBytes / (1024 * 1024), (unsigned int)diskRatio);
  379.       printf(" %8.2f\n", (float)gen->deviceBytesWritten / (1024 * 1024));
  380.  
  381.       printf("OBJECTS stream %d (clt %d) file %d dir %d rmtFile %d ioClt %d\n",
  382.         fsStats.object.streams, fsStats.object.streamClients,
  383.         fsStats.object.files, fsStats.object.directory,
  384.         fsStats.object.rmtFiles, fsStats.object.fileClients);
  385.       printf("OBJECTS  pipe %d dev %d pdevCtrl %d pdev %d remote %d Total %d\n",
  386.         fsStats.object.pipes, fsStats.object.devices,
  387.         fsStats.object.controls,
  388.         fsStats.object.pseudoStreams, fsStats.object.remote,
  389.         fsStats.object.streams + fsStats.object.files +
  390.         fsStats.object.directory +
  391.         fsStats.object.rmtFiles + fsStats.object.pipes +
  392.         fsStats.object.devices + fsStats.object.controls +
  393.         2 * fsStats.object.pseudoStreams + fsStats.object.remote);
  394.       printf("HANDLES %d (%d) limbo %d scav %d (dirs %d) looks/scav %.2f\n",
  395.         fsStats.handle.exists,
  396.         fsStats.handle.maxNumber,
  397.         fsStats.handle.limbo,
  398.         fsStats.handle.lruHits,
  399.         fsStats.object.dirFlushed,
  400.          (fsStats.handle.lruScans != 0
  401.           ? ((float)fsStats.handle.lruChecks /
  402.          (float)fsStats.handle.lruScans)
  403.           : 0.0));
  404.   }
  405.   {
  406.       register Fs_RecoveryStats *recovPtr = &fsStats.recovery;
  407.       if (recovPtr->number > 0 || recovPtr->wants > 0) {
  408.       printf("RECOVERED %d times, wants %d ok %d bad %d abort %d\n",
  409.           recovPtr->number, recovPtr->wants, recovPtr->waitOK,
  410.           recovPtr->waitFailed, recovPtr->waitAbort);
  411.       printf("RECOVERED %d handles, %d failed %d timed out\n",
  412.           recovPtr->succeeded, recovPtr->failed, recovPtr->timeout);
  413.       }
  414.       if ((recovPtr->clientRecovered + recovPtr->clientCrashed) > 0) {
  415.       printf("CLIENTS %d crashed, %d reopened\n", recovPtr->clientCrashed,
  416.           recovPtr->clientRecovered);
  417.       }
  418.   }
  419.   /*
  420.    * ONLY FILE TYPE I/O AND DELETION STUFF BELOW HERE
  421.    */
  422.   if (!printExtraFileStats) {
  423.       exit(status);
  424.   } else {
  425.       register Fs_TypeStats *type = &fsTypeStats;
  426.       register Fs_GeneralStats *gen = &fsStats.gen;
  427.       int ratio;
  428.       static char *typeStrings[] = { "temp", "swap", "obj", "bin", "other"};
  429.       /*
  430.        * # of columns for type-specific counts
  431.        */
  432.       unsigned int byteTotals[NUM_BYTE_COUNT_COLUMNS];
  433.  
  434.  
  435.       printf(
  436.            "File type       Cache(R)          Cache(W)           Disk(R)           Disk(W)");
  437.  
  438.       /*
  439.        * Calculate the totals for each column so we can get accurate fractions
  440.        * on a per-filetype basis.
  441.        */
  442.       
  443.       for (i = 0; i < NUM_BYTE_COUNT_COLUMNS; i++) {
  444.       byteTotals[i] = 0;
  445.       }
  446.       for (i = 0; i < FS_STAT_NUM_TYPES; i++) {
  447.       byteTotals [0] += type->cacheBytes[FS_STAT_READ][i];
  448.       byteTotals [1] += type->cacheBytes[FS_STAT_WRITE][i];
  449.       byteTotals [2] += type->diskBytes[FS_STAT_READ][i];
  450.       byteTotals [3] += type->diskBytes[FS_STAT_WRITE][i];
  451.       }
  452.  
  453.       for (i = 0; i < FS_STAT_NUM_TYPES; i++) {
  454.       printf("\n%-6s ", typeStrings[i]);
  455.       PrintRatio(type->cacheBytes[FS_STAT_READ][i], byteTotals[0]);
  456.       PrintRatio(type->cacheBytes[FS_STAT_WRITE][i], byteTotals[1]);
  457.       PrintRatio(type->diskBytes[FS_STAT_READ][i], byteTotals[2]);
  458.       PrintRatio(type->diskBytes[FS_STAT_WRITE][i], byteTotals[3]);
  459.       }
  460.       if (gen->fileBytesDeleted > 0) {
  461.       printf("\n\nFile type      Deleted");
  462.       for (i = 0; i < FS_STAT_NUM_TYPES; i++) {
  463.           printf("\n%-5s    ", typeStrings[i]);
  464.           PrintRatio(type->bytesDeleted[i],
  465.              gen->fileBytesDeleted);
  466.       }
  467.       printf("\nTotal       %10u 100%%\n", gen->fileBytesDeleted);
  468.       } else {
  469.       printf("\n 0 bytes deleted from local disks.\n");
  470.       }
  471.       if (deleteHist && gen->fileBytesDeleted > 0) {
  472.       register int timeIndex, sizeIndex;
  473.       int fileType;
  474.       unsigned int cumBySize[FS_HIST_SIZE_BUCKETS];
  475.       unsigned int cumByTime[FS_HIST_TIME_BUCKETS];
  476.       unsigned int cumBySizeType[FS_HIST_SIZE_BUCKETS][FS_STAT_NUM_TYPES];
  477.       unsigned int cumByTimeType[FS_HIST_TIME_BUCKETS][FS_STAT_NUM_TYPES];
  478.       register unsigned int rowTotal;
  479.       unsigned int total = 0;
  480. #ifdef notdef
  481.       unsigned int numBlocks;   /* number of blocks corresponding to an index */
  482. #endif
  483.       unsigned int blockCount;  /* number blocks in array at current location */
  484.       unsigned int tooBig;       /* subtotal of last few (unprintable) columns */
  485.       char separator[DEFAULT_WIDTH + 1];
  486.       static char *timeStrings[] = {
  487.           "1 sec ",
  488.           "2 secs",
  489.           "3 secs",
  490.           "4 secs",
  491.           "5 secs",
  492.           "6 secs",
  493.           "7 secs",
  494.           "8 secs",
  495.           "9 secs",
  496.           "10 secs",
  497.           "20 secs",
  498.           "30 secs",
  499.           "40 secs",
  500.           "50 secs",
  501.           "1 min ",
  502.           "2 mins",
  503.           "3 mins",
  504.           "4 mins",
  505.           "5 mins",
  506.           "6 mins",
  507.           "7 mins",
  508.           "8 mins",
  509.           "9 mins",
  510.           "10 mins",
  511.           "20 mins",
  512.           "30 mins",
  513.           "40 mins",
  514.           "50 mins",
  515.           "1 hr ",
  516.           "2 hrs",
  517.           "3 hrs",
  518.           "4 hrs",
  519.           "5 hrs",
  520.           "6 hrs",
  521.           "7 hrs",
  522.           "8 hrs",
  523.           "9 hrs",
  524.           "10 hrs",
  525.           "15 hrs",
  526.           "20 hrs",
  527.           "1 day ",
  528.           "2 days",
  529.           "3 days",
  530.           "4 days",
  531.           "5 days",
  532.           "6 days",
  533.           "7 days",
  534.           "8 days",
  535.           "9 days",
  536.           "10 days",
  537.           "20 days",
  538.           "30 days",
  539.           "40 days",
  540.           "50 days",
  541.           "60 days",
  542.           "90 days",
  543.           "120 days",
  544.           "180 days",
  545.           "240 days",
  546.           "300 days",
  547.           "360 days",
  548.           ">360 days",
  549.       };
  550.     
  551.       bzero((Address) cumBySize, sizeof (cumBySize));
  552.       bzero((Address) cumByTime, sizeof (cumByTime));
  553.       bzero((Address) cumBySizeType, sizeof (cumBySizeType));
  554.       bzero((Address) cumByTimeType, sizeof (cumByTimeType));
  555.  
  556.       /*
  557.        * May set times to be greater than actual max, since it is
  558.        * changing around in the kernel.
  559.        */
  560.       if (numTimesToPrint > FS_HIST_TIME_BUCKETS) {
  561.           numTimesToPrint = FS_HIST_TIME_BUCKETS;
  562.       }
  563.  
  564.       for (i = 0; i < DEFAULT_WIDTH; i++) {
  565.           separator[i] = '-';
  566.       }
  567.       separator[DEFAULT_WIDTH] = '\0';
  568.  
  569.       /*
  570.        * Go through the histogram and for each time, print out one line
  571.        * per file type with all the counts for that file type.
  572.        * Then print out a summary line for that time period.
  573.        * Since we only print out the first several columns, if we
  574.        * have data for files too big to display normaly, we lump them into
  575.        * a single column at the end.
  576.        */
  577.     
  578.       printf(
  579.     "\n\014\nDeletion histogram:%59s\n <= #secs   <1K <2K <4K <8K 16K ...%43s",
  580.            "Wtd", ">>  Total");
  581.       for (timeIndex = 0; timeIndex < FS_HIST_TIME_BUCKETS; timeIndex++) {
  582.           if (timeIndex < numTimesToPrint) {
  583.           printf("\n%s\n", separator);
  584.           }
  585.           for (fileType = 0; fileType < FS_STAT_NUM_TYPES; fileType ++) {
  586.           tooBig = 0;
  587.           if (timeIndex < numTimesToPrint) {
  588.               printf("%10s  ", typeStrings[fileType]);
  589.           }
  590.           if (timeIndex > 0) {
  591.               cumByTimeType[timeIndex][fileType] =
  592.                   cumByTimeType[timeIndex - 1][fileType];
  593.           }
  594.           for (sizeIndex = 0; sizeIndex < FS_HIST_SIZE_BUCKETS - 1;
  595.                sizeIndex++) {
  596.                blockCount = type->deleteHist[timeIndex][sizeIndex]
  597.                [fileType];
  598.                if (blockCount > 0) {
  599.                cumBySizeType[sizeIndex][fileType] += blockCount;
  600.                cumBySize[sizeIndex] += blockCount;
  601.                }
  602.                if ((timeIndex < numTimesToPrint) &&
  603.                (sizeIndex < numSizesToPrint - 1)) {
  604.                printf("%3u ", cumBySizeType[sizeIndex][fileType]);
  605.                } else if (blockCount > 0) {
  606.                if (sizeIndex >= numSizesToPrint - 1) {
  607.                    tooBig += blockCount;
  608.                } else {
  609.                    fprintf(stderr,
  610.                       "Warning: element <%u,%u,%u> = %u.\n",
  611.                       timeIndex, sizeIndex, fileType,
  612.                       blockCount);
  613.                }
  614.                }
  615.            }
  616.           rowTotal = type->deleteHist[timeIndex]
  617.               [FS_HIST_SIZE_BUCKETS-1][fileType];
  618.           cumByTimeType[timeIndex][fileType] += rowTotal;
  619.           total += rowTotal;
  620.           cumByTime[timeIndex] += rowTotal;
  621.           if (timeIndex < numTimesToPrint) {
  622.               printf("%3u %6u\n", tooBig,
  623.                    cumByTimeType[timeIndex][fileType]);
  624.           }
  625.           }
  626.           /*
  627.            * Print out subtotals for this time.
  628.            */
  629.           tooBig = 0;
  630.           if (timeIndex < numTimesToPrint) {
  631.           printf("%9s   ", timeStrings[timeIndex]);
  632.           for (sizeIndex = 0; sizeIndex < FS_HIST_SIZE_BUCKETS - 1;
  633.                sizeIndex++) {
  634.                if (sizeIndex < numSizesToPrint - 1) {
  635.                printf("%3u ", cumBySize[sizeIndex]);
  636.                } else if (sizeIndex >= numSizesToPrint) {
  637.                tooBig += blockCount;
  638.                }
  639.            }
  640.           printf("%3u %6u", tooBig, total);
  641.           }
  642.       }
  643.       printf("\n\014\nPercentiles for bytes deleted, by type:\n\n");
  644.       printf("      Time   %6s %6s %6s %6s %6s   Cumulative         Cum. Total\n",
  645.            typeStrings[0],  typeStrings[1],
  646.            typeStrings[2], typeStrings[3], typeStrings[4]);
  647.       for (timeIndex = 0, rowTotal = 0;
  648.            timeIndex < numTimesToPrint; timeIndex++) {
  649.            rowTotal += cumByTime[timeIndex];
  650.            printf("%10s    ", timeStrings[timeIndex]);
  651.            for (fileType = 0; fileType < FS_STAT_NUM_TYPES; fileType++) {
  652.            ratio = GetRatio(cumByTimeType[timeIndex][fileType],
  653.                   cumByTimeType[FS_HIST_TIME_BUCKETS - 1]
  654.                              [fileType]);
  655.            printf("%5u  ", ratio);
  656.            }
  657.            printf("      %5u          %9u\n",
  658.             GetRatio(rowTotal, total), rowTotal);
  659.        }
  660.       }
  661.         
  662.   }
  663.   exit(status);
  664. }
  665.  
  666.  
  667.  
  668.  
  669. /*
  670.  *----------------------------------------------------------------------
  671.  *
  672.  * GetRatio --
  673.  *
  674.  *    Given two integers, return an integer that is 100 * the quotient of
  675.  *    the two numbers, or 0 if the first number is 0, or 100 if the
  676.  *    first number is greater than the second number.
  677.  *
  678.  * Results:
  679.  *    The ratio, normalized to a percentage, is returned.
  680.  *
  681.  * Side effects:
  682.  *    None.
  683.  *
  684.  *----------------------------------------------------------------------
  685.  */
  686.  
  687. int
  688. GetRatio(numerator, denominator)
  689.     unsigned int numerator;
  690.     unsigned int denominator;
  691. {
  692.     double ratio; 
  693.             
  694.     if (numerator > denominator) { 
  695.     return(100); 
  696.     }
  697.     if (denominator > 0 && numerator > 0) { 
  698.     ratio = (100. * numerator) / denominator + 0.5;
  699.     if (ratio < 0) {
  700.         ratio = (100000. * (numerator/1000)) /
  701.             (1000. * (denominator/1000)) + 0.5;
  702.     }
  703.     } else { 
  704.     ratio = 0.; 
  705.     }
  706.     return ((int) ratio);
  707.     
  708.